package helper

import (
	// used by beego/orm
	"database/sql"
	//"github.com/astaxie/beego/orm"
	"github.com/beego/beego/v2/client/orm"
	// go-sql-driver
	"Hypnos-Soundcore/components/config"
	"Hypnos-Soundcore/constants"
	"github.com/gin-gonic/gin"
	_ "github.com/go-sql-driver/mysql"
	"io"
	"strings"
	"time"
)

var mysqlConf config.MysqlConfig

// InitMysql init mysql
func InitMysql(xconf *config.XConfig) error {
	mysqlConf = xconf.Mysql
	// orm.DefaultRowsLimit = DefaultNum
	var dbErr error
	if xconf.Mysql.Debug {
		orm.Debug = true
		orm.DebugLog = orm.NewLog(gin.DefaultWriter)
	}
	for name, dbConf := range xconf.Mysql.Dbs {
		dbErr = orm.RegisterDataBase(name, constants.DriverMysql, dbConf.DbConf)
		if dbErr == nil {
			if dbConf.MaxIdleConns > 0 {
				orm.SetMaxIdleConns(name, dbConf.MaxIdleConns)
			} else {
				orm.SetMaxIdleConns(name, 10)
			}
			if dbConf.MaxOpenConns > 0 {
				orm.SetMaxOpenConns(name, dbConf.MaxOpenConns)
			} else {
				orm.SetMaxOpenConns(name, 100)
			}
			if dbConf.ConnMaxLifeTime > 0 {
				db, err := orm.GetDB(name)
				if err == nil {
					db.SetConnMaxLifetime(time.Second * time.Duration(dbConf.ConnMaxLifeTime))
				}
			}
		} else {
			break
		}
	}
	return dbErr
}

// UninitMysql release mysql
func UninitMysql() {
	for _, name := range constants.DBNames {
		WarningActionPrintln(constants.TagHelper, constants.ActionCloseDB, name, "", 0)
		db, err := orm.GetDB(name)
		if err != nil {
			WarningActionPrintln(constants.TagHelper, constants.ActionGetDB, name, err.Error(), 0)
		} else if db != nil {
			err = db.Close()
			if err != nil {
				WarningActionPrintln(constants.TagHelper, constants.ActionCloseDB, name, err.Error(), 0)
			}
		}
	}
}

// SetLogger set orm logger, just for debug
func SetLogger(out io.Writer) {
	orm.Debug = true
	orm.DebugLog = orm.NewLog(out)
}

// NewOrm create new orm with default name
func NewOrm() (orm.Ormer, error) {
	return NewOrmWithName(constants.DbDefault)
}

// NewReadonlyOrm create new orm with default name
func NewReadonlyOrm() (orm.Ormer, error) {
	return NewOrmWithName(constants.DBReadonlyName(constants.DbDefault))
}

// NewProxyOrm create new orm with default name
func NewProxyOrm() (orm.Ormer, error) {
	return NewOrmWithName(constants.DBProxyName(constants.DbDefault))
}

// NewOrmWithName create new orm with specified name
func NewOrmWithName(name string) (orm.Ormer, error) {
	var err error
	o := orm.NewOrm()
	if strings.Compare(name, constants.DbDefault) != 0 {
		//err = o.Using(name)
		if err != nil {
			o = nil
		}
	}
	return o, err
}

// NewReadonlyOrmWithName create new orm with specified name
func NewReadonlyOrmWithName(name string) (orm.Ormer, error) {
	return NewOrmWithName(constants.DBReadonlyName(name))
}

// NewProxyOrmWithName create new orm with specified name
func NewProxyOrmWithName(name string) (orm.Ormer, error) {
	return NewOrmWithName(constants.DBProxyName(name))
}

// GetDBWithName get db with specified name
func GetDBWithName(name string) (*sql.DB, error) {
	return orm.GetDB(name)
}

// GetDefaultDB get default db
func GetDefaultDB() (*sql.DB, error) {
	return orm.GetDB(constants.DbDefault)
}

// Begin .
func Begin(o orm.Ormer) (orm.TxOrmer, error) {
	return o.Begin()
}

// Commit .
func Commit(o orm.TxOrmer) (err error) {
	if err = o.Commit(); err != nil {
		_ = o.Rollback()
	}
	return err
}

// Rollback .
func Rollback(o orm.TxOrmer) error {
	err := o.Rollback()
	if err != nil {
		ErrorActionPrintf(constants.TagHelper, "Rollback", "err: %v", err)
	}
	return err
}

// IsShardingV1 拆分数据库过渡版本v1，只从原数据分库查询，insert/update/delete除原数据分库(环1)执行一次外，新数据分库(环2)也要再执行一次，并将新数据分库操作失败记录写入同步表中
func IsShardingV1() bool {
	return false
	//return config.GetXConfig().Mysql.Sharding == "v1"
}

// IsShardingV2 拆分数据库过渡版本v2，只请求新数据分库，但查询分库表时需要将limit乘2，并在查询后的数据进行去重
func IsShardingV2() bool {
	return false
	//return config.GetXConfig().Mysql.Sharding == "v2"
}

// UsingSnowFlakeID 是否使用snowflake生成id
func UsingSnowFlakeID() bool {
	return false
	//return config.GetXConfig().Mysql.SnowFlakeID
}

// GenerateInt64ID 是否生成64位id
func GenerateInt64ID() bool {
	return config.GetXConfig().Mysql.GenerateInt64ID
}

/*
*
获取分布式数据库ORM 主要考虑不同表分布不同的数据库，不同用户不同数据库，及其他环境兼容性,方便后期扩展
*/
func GetShardingOrm(isSharing func(string) bool, tableName, userID string, userRangeID int64) (orm.Ormer, error) {
	if !isSharing(tableName) {
		return NewOrm()
	}
	return NewOrmWithName("sharding")
}

// ParseShardDB 解析分库名和表序号
func ParseShardDB(userRangeID int64) (string, int) {
	for dbname, dbConf := range mysqlConf.Dbs {
		if len(dbConf.ShardRange) > 1 {
			if userRangeID >= dbConf.ShardRange[0] && userRangeID <= dbConf.ShardRange[1] {
				tableNo := userRangeID%40 + 1
				return dbname, int(tableNo)
			}
		}
	}
	return "", 0
}

// GetDBShards 获取分库名称数组
func GetDBShards() []string {
	return mysqlConf.DBShards
}
